#include "MySQLDao.h"

#include <memory>
#include "inicpp.hpp"
using namespace std;

MySQLPool::MySQLPool(const std::string& url, const std::string& user, const std::string& password,
                     const std::string& schema, const int pool_size)
    : _url(url), _user(user), _password(password), _schema(schema), _pool_size(pool_size), _b_stop(false) {
    for (int i = 0; i < _pool_size; i++) {
        sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance();
        std::unique_ptr<sql::Connection> con(driver->connect(_url, _user, _password));
        con->setSchema(_schema);
        _pool.push(std::move(con));
    }
    std::clog << "MySQL连接成功，Host: " << url << " ，User: " << user << " ，Password: " << password << " ，Schema: " << schema
        <<
        " ，PoolSize: " << pool_size << std::endl;
}

MySQLPool::~MySQLPool() {
    std::unique_lock<std::mutex> lock(_mutex);
    while (!_pool.empty()) {
        _pool.pop();
    }
}

std::unique_ptr<sql::Connection> MySQLPool::GetConnection() {
    std::unique_lock<std::mutex> lock(_mutex);
    _cond.wait(lock, [this] {
        if (_b_stop) {
            return true;
        }
        return !_pool.empty();
    });
    if (_b_stop) {
        return nullptr;
    }
    std::unique_ptr<sql::Connection> con(std::move(_pool.front()));
    _pool.pop();
    return con;
}

void MySQLPool::ReturnConnection(std::unique_ptr<sql::Connection> con) {
    std::unique_lock<std::mutex> lock(_mutex);
    if (_b_stop) {
        return;
    }
    _pool.push(std::move(con));
    _cond.notify_one();
}

void MySQLPool::Close() {
    _b_stop = true;
    _cond.notify_all();
}

MySQLDao::MySQLDao() {
    //读取配置文件
    inicpp::IniManager _ini("../config/config.ini");
    if (!_ini.isSectionExists("Mysql")) {
        std::cout << "section of Server: not exist" << std::endl;
    }
    if (!_ini["Mysql"].isKeyExist("Host") || !_ini["Mysql"].isKeyExist("Port")) {
        std::cout << "Server.Port: not exist!" << std::endl;
    }
    const auto& host = _ini["Mysql"]["Host"];
    const auto& port = _ini["Mysql"]["Port"];
    const auto& user = _ini["Mysql"]["User"];
    const auto& pwd = _ini["Mysql"]["Password"];
    const auto& schema = _ini["Mysql"]["Schema"];
    const auto& PoolSize = _ini["Mysql"].toInt("PoolSize");
    _pool = std::make_unique<MySQLPool>(host + ":" + port, user, pwd, schema, PoolSize);
}

MySQLDao::~MySQLDao() {
    _pool->Close();
    clog << "MySQLDao::~MySQLDao()" << std::endl;
}

bool MySQLDao::UserLogin(const std::string& user, const std::string& password) const {
    auto con = _pool->GetConnection();
    try {
        if (con == nullptr) {
            _pool->ReturnConnection(std::move(con));
            return false;
        }
        const std::unique_ptr<sql::PreparedStatement> stmt(
            con->prepareStatement("select count(*) from users where username = ? and password = ?"));
        stmt->setString(1, user);
        stmt->setString(2, password);
        stmt->execute();
        // 获取结果集
        const std::unique_ptr<sql::ResultSet> result(stmt->getResultSet());

        // 检查是否有结果
        if (result->next()) {
            // 获取结果
            const int count = result->getInt(1);
            if (count == 1) {
                return true;
            }
        }
        else {
            std::cerr << "No matching records found." << std::endl;
        }
        return false;
    }
    catch (sql::SQLException& e) {
        _pool->ReturnConnection(std::move(con));
        std::cerr << "SQLException: " << e.what();
        std::cerr << " (MySQL error code: " << e.getErrorCode();
        std::cerr << ", SQLState: " << e.getSQLState() << " )" << std::endl;
        return false;
    }
}

void MySQLDao::SelectSql(const std::string& sql) const {
    auto con = _pool->GetConnection();
    const unique_ptr<sql::Statement> select(con->createStatement());

    // 执行查询
    const std::unique_ptr<sql::ResultSet> result(select->executeQuery(sql));

    // 遍历结果集
    while (result->next()) {
        std::cout << "Check Email: " << result->getString("email") << std::endl;
    }
    _pool->ReturnConnection(std::move(con));
}

int MySQLDao::RegUser(const std::string& name, const std::string& email, const std::string& pwd) const {
    auto con = _pool->GetConnection();
    try {
        if (con == nullptr) {
            _pool->ReturnConnection(std::move(con));
            return false;
        }
        // 准备调用存储过程
        const std::unique_ptr<sql::PreparedStatement> stmt(con->prepareStatement("CALL reg_user(?,?,?,@result)"));
        // 设置输入参数
        stmt->setString(1, name);
        stmt->setString(2, email);
        stmt->setString(3, pwd);
        // 由于 PreparedStatement 不直接支持注册输出参数，我们需要使用会话变量或其他方法来获取输出参数的值
        // 执行存储过程
        stmt->execute();
        // 如果存储过程设置了会话变量或有其他方式获取输出参数的值，你可以在这里执行SELECT查询来获取它们
        // 例如，如果存储过程设置了一个会话变量@result来存储输出结果，可以这样获取：
        const unique_ptr<sql::Statement> stmtResult(con->createStatement());
        const unique_ptr<sql::ResultSet> res(stmtResult->executeQuery("SELECT @result AS result"));
        if (res->next()) {
            const int result = res->getInt("result");
            cout << "Result: " << result << endl;
            _pool->ReturnConnection(std::move(con));
            return result;
        }
        _pool->ReturnConnection(std::move(con));
        return -1;
    }
    catch (sql::SQLException& e) {
        _pool->ReturnConnection(std::move(con));
        std::cerr << "SQLException: " << e.what();
        std::cerr << " (MySQL error code: " << e.getErrorCode();
        std::cerr << ", SQLState: " << e.getSQLState() << " )" << std::endl;
        return -1;
    }
}
